package com.demo.component.bean;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    public byte[] getByte(byte[] key) {
        byte[] result = stringRedisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] value = connection.get(key);
                return value;
            }
        });
        return result;
    }

    public byte[] getByte(String key) {
        byte[] result = stringRedisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer();
                byte[] value = connection.get(serializer.serialize(key));
                return value;
            }
        });
        return result;
    }

    public boolean setByte(byte[] key, byte[] value) {
        boolean result = stringRedisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key, value);
                return true;
            }
        });
        return result;
    }

    public boolean setByte(String key, String value) {
        boolean result = stringRedisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer();
                connection.set(serializer.serialize(key), serializer.serialize(value));
                return true;
            }
        });
        return result;
    }

    /**
     * 设置区间
     * @param key
     * @param valueMap
     * @return
     */
    public boolean setRange(String key, Map<String, Double> valueMap){
        try {
            if (CollectionUtils.isEmpty(valueMap) || StringUtils.isBlank(key)) {
                return false;
            }
            ZSetOperations<String, String> vo = redisTemplate.opsForZSet();
            for (String value : valueMap.keySet()) {
                vo.add(key, value, valueMap.get(value));
            }
        }catch(Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 设置排序集合
     *
     * @param key
     * @param score
     * @param value
     * @return
     */
    public boolean setSortedSet(String key, double score, String value) {
        try {
            ZSetOperations<String, String> vo = redisTemplate.opsForZSet();
            vo.add(key, value, score);
        }catch(Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取区间中第一个大于等于min的数据
     * @param key
     * @param min
     * @return
     */
    public String getFirstEnd(String key, Double min){
        Set<String> set = getRange(key, min, Double.MAX_VALUE, 0, 1); //包含的
        if (CollectionUtils.isEmpty(set)) {
            return null;
        }
        return set.toArray()[0].toString();
    }

    /**
     * 获取区间中大于等于min且小于等于max的，偏移量为offset的最多count个数据
     * @param key redis的key
     * @param min 最小值
     * @param max 最大值
     * @param offset 偏移量
     * @param count 匹配数据个数
     * @return
     */
    public Set<String> getRange(String key, Double min, Double max, int offset, int count){
        try {
            ZSetOperations<String, String> vo = redisTemplate.opsForZSet();
            Set<String> set = vo.rangeByScore(key, min, max, offset, count); //包含的
            return set;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 自动递增
     * @param key
     * @param expires 过期时间 单位：秒
     * @return
     */
    public Long incr(String key, long expires){
        return incr(key, expires, 1L);
    }

    /**
     * 自动递增
     * @param key
     * @param expires 过期时间 单位：秒
     * @param upNum 增涨数
     * @return
     */
    public Long incr(String key, long expires, long upNum){
        String oldValue = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(oldValue)) {
            if (expires == 0) {
                stringRedisTemplate.opsForValue().set(key, "0");
            } else {
                stringRedisTemplate.opsForValue().set(key, "0", expires, TimeUnit.SECONDS);
            }
        }
        return stringRedisTemplate.opsForValue().increment(key, upNum);
    }

    /**
     * 自动递减
     * @param key
     * @param expires 过期时间 单位：秒
     * @return
     */
    public Long decr(String key, long expires){
        String oldValue = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(oldValue) || Long.parseLong(oldValue) < 1) {
            if (expires == 0) {
                stringRedisTemplate.opsForValue().set(key, "0");
            } else {
                stringRedisTemplate.opsForValue().set(key, "0", expires, TimeUnit.SECONDS);
            }
        }
        Long num = stringRedisTemplate.opsForValue().increment(key, -1L) ;
        return num < 0 ? 0 : num;
    }

    /**
     * 根据key模糊删除
     * @param key 索要删除的数据key的前缀
     * @return
     */
    public void dimDel(String key) {
        try {
            Set<String> keys = redisTemplate.keys(key + "*");
            redisTemplate.delete(keys);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将数据设置到缓存（redis）中
     * @param key
     * @param expires 单位:秒，0 表示永不过期
     * @param value
     * @param <T>
     */
    public <T> boolean set(String key, int expires, T value) {
        try {
            if (expires <= 0) {
                redisTemplate.opsForValue().set(key, value);
            } else {
                redisTemplate.opsForValue().set(key, value, (long) expires, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取缓存(redis)的数据
     * @param key
     * @return
     */
    public <T> T get(String key) {
        try{
            Object json = redisTemplate.opsForValue().get(key);
            if (json != null) {
                return (T) json;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 清除缓存（redis）
     * @param key
     */
    public boolean delete(String key) {
        if (StringUtils.isEmpty(key)) return false;
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}
